package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mysql.utils.PageUtils;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.handler.OrdersHandler;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.request.OrderPageQueryReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OperationOrdersDetailResDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;
import static com.jzo2o.orders.base.constants.RedisConstants.RedisKey.ORDERS;
import static com.jzo2o.orders.base.constants.RedisConstants.Ttl.ORDERS_PAGE_TTL;
/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author ithyfjs
 * @since 2024-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Resource
    private OrdersManagerServiceImpl owner;

    @Resource
    private IOrdersCanceledService ordersCanceledService;

    @Resource
    private IOrdersCommonService ordersCommonService;

    @Resource
    private IOrdersCreateService ordersCreateService;

    @Resource
    private IOrdersRefundService ordersRefundService;

    @Resource
    private OrdersHandler ordersHandler;

    @Resource
    private OrderStateMachine orderStateMachine;

    @Resource
    private CacheHelper cacheHelper;

    //派单中状态取消订单
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO orderCancelDTO,
                                    Long tradingOrderNo,
                                    BigDecimal realPayAmount) {
        //保存取消订单记录(与未支付的一样)
        OrdersCanceled ordersCanceled = BeanUtil.toBean(orderCancelDTO, OrdersCanceled.class);
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);
        //更新订单状态为关闭订(状态与未支付的不一样)
        OrderUpdateStatusDTO orderUpdateStatusDTO = OrderUpdateStatusDTO.builder()
                .id(orderCancelDTO.getId())
                .originStatus(OrderStatusEnum.DISPATCHING.getStatus())
                .targetStatus(OrderStatusEnum.CLOSED.getStatus())
                .refundStatus(OrderRefundStatusEnum.REFUNDING.getStatus())//退款状态为退款中
                .build();
        int result = ordersCommonService.updateStatus(orderUpdateStatusDTO);
        if (result <= 0) {
            throw new DbRuntimeException("待服务订单关闭事件处理失败");
        }
        //添加退款记录(相对未支付逻辑，这里是新增的)
        OrdersRefund ordersRefund = new OrdersRefund();
        ordersRefund.setId(orderCancelDTO.getId());
        ordersRefund.setTradingOrderNo(tradingOrderNo);
        ordersRefund.setRealPayAmount(realPayAmount);
        ordersRefundService.save(ordersRefund);
    }

    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
        //从快照表查询快照
        String currentSnapshotJson = orderStateMachine.getCurrentSnapshotCache(String.valueOf(id));
        OrderSnapshotDTO orderSnapshotDTO = JsonUtils.toBean(currentSnapshotJson, OrderSnapshotDTO.class);
        //懒加载方式取消支付超时的 订单
        orderSnapshotDTO = canalIfPayOvertime(orderSnapshotDTO);

        OrderResDTO orderResDTO = BeanUtil.toBean(orderSnapshotDTO, OrderResDTO.class);

        return orderResDTO;
    }

    /**
     * 如果支付过期则取消订单
     * @param orders
     */
    private Orders canalIfPayOvertime(Orders orders){
        //创建订单未支付15分钟后自动取消
        if(Objects.equals(orders.getOrdersStatus(), OrderStatusEnum.NO_PAY.getStatus())
                && orders.getCreateTime().plusMinutes(15).isBefore(LocalDateTime.now())){
            // 查询支付结果，如果支付最新状态仍是未支付进行取消订单
            // 避免我们点击详情的时候，有好心人帮我们支付好了
            OrdersPayResDTO ordersPayResDTO = ordersCreateService.getPayResultFromTradServer(orders.getId());
            int payResultFromTradServer = ordersPayResDTO.getPayStatus();
            if(payResultFromTradServer != OrderPayStatusEnum.PAY_SUCCESS.getStatus()){
                //取消订单
                OrderCancelDTO orderCancelDTO = BeanUtil.toBean(orders, OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("订单超时支付，自动取消");
                // 调用前面写好的方法(里面自动实现已支付-未支付的取消逻辑)
                cancel(orderCancelDTO);
                orders = getById(orders.getId());
            }
        }
        return orders;
    }

    /**
     * 如果支付过期则取消订单
     * @param orderSnapshotDTO
     */
    public OrderSnapshotDTO canalIfPayOvertime(OrderSnapshotDTO orderSnapshotDTO){
        //订单状态
        Integer ordersStatus = orderSnapshotDTO.getOrdersStatus();
        //判断订单是未支付且支付超时(从订单创建时间开始15分钟未支付)
        //        if(ordersStatus==OrderStatusEnum.NO_PAY.getStatus() && orders.getCreateTime().isBefore(LocalDateTime.now().minusMinutes(15)) ){
        if(Objects.equals(ordersStatus, OrderStatusEnum.NO_PAY.getStatus()) && orderSnapshotDTO.getCreateTime().plusMinutes(15).isBefore(LocalDateTime.now())){
            //查询一下最新的支付状态，如果没有支付成功，再执行下边的取消代码
            OrdersPayResDTO payResultFromTradServer = ordersCreateService.getPayResultFromTradServer(orderSnapshotDTO.getId());
            //如果没有支付成功，再执行下边的取消代码
            if(ObjectUtils.isNotNull(payResultFromTradServer) && payResultFromTradServer.getPayStatus()!= OrderPayStatusEnum.PAY_SUCCESS.getStatus()){
                OrderCancelDTO orderCancelDTO = BeanUtils.toBean(orderSnapshotDTO,OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("订单支付超时系统自动取消");
                cancelByNoPay(orderCancelDTO);

                //从快照中查询订单数据
                String jsonResult = orderStateMachine.getCurrentSnapshotCache(String.valueOf(orderSnapshotDTO.getId()));
                orderSnapshotDTO = JSONUtil.toBean(jsonResult, OrderSnapshotDTO.class);
                return orderSnapshotDTO;
            }
        }
        return orderSnapshotDTO;
    }

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }
    /**
     * 订单信息聚合
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OperationOrdersDetailResDTO aggregation(Long id) {
       return null;
    }
    /**
     * 管理端 - 分页查询订单id列表
     *
     * @param orderPageQueryReqDTO 分页查询模型
     * @return 分页结果
     */
    @Override
    public Page<Long> operationPageQueryOrdersIdList(OrderPageQueryReqDTO orderPageQueryReqDTO) {
        //1.构造查询条件
        Page<Orders> page = PageUtils.parsePageQuery(orderPageQueryReqDTO, Orders.class);
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtil.isNotEmpty(orderPageQueryReqDTO.getContactsPhone()), Orders::getContactsPhone, orderPageQueryReqDTO.getContactsPhone())
                .eq(ObjectUtil.isNotEmpty(orderPageQueryReqDTO.getOrdersStatus()), Orders::getOrdersStatus, orderPageQueryReqDTO.getOrdersStatus())
                .eq(ObjectUtil.isNotEmpty(orderPageQueryReqDTO.getPayStatus()), Orders::getPayStatus, orderPageQueryReqDTO.getPayStatus())
                .eq(ObjectUtil.isNotEmpty(orderPageQueryReqDTO.getRefundStatus()), Orders::getRefundStatus, orderPageQueryReqDTO.getRefundStatus())
                .between(ObjectUtil.isAllNotEmpty(orderPageQueryReqDTO.getMinCreateTime(), orderPageQueryReqDTO.getMaxCreateTime()), Orders::getCreateTime, orderPageQueryReqDTO.getMinCreateTime(), orderPageQueryReqDTO.getMaxCreateTime())
                .gt(Orders::getUserId, 0)
                .select(Orders::getId);

        if (ObjectUtil.isNotEmpty(orderPageQueryReqDTO.getId())) {
            queryWrapper.eq(Orders::getId, orderPageQueryReqDTO.getId());
        } else {
            queryWrapper.gt(Orders::getId, 0);
        }

        //2.分页查询
        Page<Orders> ordersPage = baseMapper.selectPage(page, queryWrapper);

        //3.封装结果，查询数据为空，直接返回
        Page<Long> orderIdsPage = new Page<>();
        BeanUtil.copyProperties(ordersPage, orderIdsPage, "records");
        if (ObjectUtil.isEmpty(ordersPage.getRecords())) {
            return orderIdsPage;
        }

        //4.查询结果不为空，提取订单id封装
        List<Long> orderIdList = ordersPage.getRecords().stream().map(Orders::getId).collect(Collectors.toList());
        orderIdsPage.setRecords(orderIdList);
        return orderIdsPage;
    }

    /**
     * 根据订单id列表查询并排序
     *
     * @param orderPageQueryReqDTO 订单分页查询请求
     * @return 订单列表
     */
    @Override
    public List<Orders> queryAndSortOrdersListByIds(OrderPageQueryReqDTO orderPageQueryReqDTO) {
        //1.构造查询条件
        Page<Orders> page = new Page<>();
        page.setSize(orderPageQueryReqDTO.getPageSize());
        page.setOrders(PageUtils.getOrderItems(orderPageQueryReqDTO, Orders.class));
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .in(Orders::getId, orderPageQueryReqDTO.getOrdersIdList())
                .eq(ObjectUtils.isNotNull(orderPageQueryReqDTO.getUserId()), Orders::getUserId, orderPageQueryReqDTO.getUserId())
                .gt(ObjectUtils.isNull(orderPageQueryReqDTO.getUserId()), Orders::getUserId, 0);

        //2.分页查询
        page.setSearchCount(false);
        Page<Orders> ordersPage = baseMapper.selectPage(page, queryWrapper);
        if (ObjectUtil.isEmpty(ordersPage.getRecords())) {
            return Collections.emptyList();
        }

        return ordersPage.getRecords();
    }
    /**
     * 管理端 - 分页查询
     * @param orderPageQueryReqDTO 分页查询条件
     * @return 分页结果
     */
    @Override
    public PageResult<OrderSimpleResDTO> operationPageQuery(OrderPageQueryReqDTO orderPageQueryReqDTO) {
        //1.分页查询订单id列表
        Page<Long> ordersIdPage = operationPageQueryOrdersIdList(orderPageQueryReqDTO);
        if (ObjectUtil.isEmpty(ordersIdPage.getRecords())) {
            return PageUtils.toPage(ordersIdPage, OrderSimpleResDTO.class);
        }

        //2.根据订单id列表查询订单
        orderPageQueryReqDTO.setOrdersIdList(ordersIdPage.getRecords());
        List<Orders> ordersList = queryAndSortOrdersListByIds(orderPageQueryReqDTO);
        List<OrderSimpleResDTO> orderSimpleResDTOList = BeanUtil.copyToList(ordersList, OrderSimpleResDTO.class);

        //3.封装响应结果
        return PageResult.<OrderSimpleResDTO>builder()
                .total(ordersIdPage.getTotal())
                .pages(ordersIdPage.getPages())
                .list(orderSimpleResDTOList)
                .build();
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus())
                .select(Orders::getId);//调整1：只查询id列
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //查询订单id列表。。调整2：只查询id列
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        if (ObjectUtil.isEmpty(ordersPage.getRecords())) {
            return new ArrayList<>();
        }
        //提取订单id列表
        List<Long> orderIds= CollUtils.getFieldValues(ordersPage.getRecords(), Orders::getId);
        //todo: 先查询缓存，缓存没有再查询数据库....
        //参数1：redisKey的一部分
        String redisKey = String.format(ORDERS, currentUserId);
        /**
         * key：hash的大key
         * ids：hash的小key(订单id列表)
         * batchDataQueryExecutor：你给我一个ids集合，如果我的缓存中没有，我就会返回没有命中缓存的nocaheIds
         *  这个执行器，就会用当前没有命中的id集合去数据库查询一次
         * clazz：缓存中返回的数据对象应该是什么样
         * ttl：缓存过期时间
         */
        List<OrderSimpleResDTO> orderSimpleResDTOS = cacheHelper.batchGet(redisKey, orderIds, (nocaheIds, clazz) -> {
            // 5.1 如果缓存没有就查询数据库
            List<Orders> orders = batchQuery(nocaheIds);
            // 5.2 避免缓存穿透所以缓存空值
            if (CollectionUtil.isEmpty(orders)) {
                return new HashMap<>();
            }
            // 5.3 换数据并返回
            return orders.stream().collect(Collectors.toMap(Orders::getId, o -> BeanUtil.toBean(o, OrderSimpleResDTO.class)));
        }, OrderSimpleResDTO.class, ORDERS_PAGE_TTL);

        return orderSimpleResDTOS;
    }

    /**
     * 订单评价
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

    /**
     * 取消订单
     * @param orderCancelDTO 取消订单模型
     */
    @Override
    public void cancel(OrderCancelDTO orderCancelDTO) {
        //订单id
        Long orderId = orderCancelDTO.getId();

        //判断该订单是否存在
        Orders orders = getById(orderId);
        if (ObjectUtils.isNull(orders)) {
            throw new CommonException("要取消的订单不存在");
        }
        //将订单中的交易单号信息拷贝到orderCancelDTO
        orderCancelDTO.setTradingOrderNo(orders.getTradingOrderNo());
        orderCancelDTO.setRealPayAmount(orders.getRealPayAmount());
        //取出订单状态
        Integer ordersStatus = orders.getOrdersStatus();

        //对未支付订单的取消操作
        if(ordersStatus== OrderStatusEnum.NO_PAY.getStatus()){
            owner.cancelByNoPay(orderCancelDTO);
        }else if(ordersStatus== OrderStatusEnum.DISPATCHING.getStatus()){ //对已支付(派单中)订单的操作
            //向数据库保存三条记录
            owner.cancelByDispatching(orderCancelDTO);
            //新启一个线程请求支付服务进行退款
            ordersHandler.requestRefundNewThread(orderCancelDTO.getId());
        }else{
            throw new CommonException("当前订单状态不支付取消操作");
        }
    }

    /**
     * 取消未支付的订单
     * @param orderCancelDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByNoPay(OrderCancelDTO orderCancelDTO) {

        //添加订单取消记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(orderCancelDTO,OrdersCanceled.class);
        //取消人id
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        //取消人名称
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        //取消人的类型
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        //取消时间
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        //修改订单状态为已取消
        OrderUpdateStatusDTO orderUpdateStatusReqDTO = new OrderUpdateStatusDTO();
        //填充订单id
        orderUpdateStatusReqDTO.setId(orderCancelDTO.getId());
        //将未支付的状态最终改为已取消
        //原始状态-未支付
        orderUpdateStatusReqDTO.setOriginStatus(OrderStatusEnum.NO_PAY.getStatus());
        //目标状态-已取消
        orderUpdateStatusReqDTO.setTargetStatus(OrderStatusEnum.CANCELED.getStatus());
        //最终的执行sql: update orders set orders_status=600 where id=? and orders_status=0
        Integer integer = ordersCommonService.updateStatus(orderUpdateStatusReqDTO);
        if(integer<=0){
            throw new DbRuntimeException("订单取消事件处理失败");
        }
    }

    /**
     * 取消派单中的订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO orderCancelDTO){
        //添加订单取消记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(orderCancelDTO,OrdersCanceled.class);
        //取消人id
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        //取消人名称
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        //取消人的类型
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        //取消时间
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        //修改订单状态为已关闭
        OrderUpdateStatusDTO orderUpdateStatusReqDTO = new OrderUpdateStatusDTO();
        //填充订单id
        orderUpdateStatusReqDTO.setId(orderCancelDTO.getId());
        //将未支付的状态最终改为已取消
        //原始状态-派单中
        orderUpdateStatusReqDTO.setOriginStatus(OrderStatusEnum.DISPATCHING.getStatus());
        //目标状态-已关闭
        orderUpdateStatusReqDTO.setTargetStatus(OrderStatusEnum.CLOSED.getStatus());
        //设置退款状态为1退款中
        orderUpdateStatusReqDTO.setRefundStatus(OrderRefundStatusEnum.REFUNDING.getStatus());
        //最终的执行sql: update orders set orders_status=700 where id=? and orders_status=100
        Integer integer = ordersCommonService.updateStatus(orderUpdateStatusReqDTO);
        if(integer<=0){
            throw new DbRuntimeException("订单取消事件处理失败");
        }

        //保存退款记录
        OrdersRefund ordersRefund = BeanUtils.toBean(orderCancelDTO,OrdersRefund.class);
        ordersRefundService.save(ordersRefund);


    }

}
